require 'pp' 
#require 'logger'
#require 'benchmark'
   
#@@logger = Logger.new(STDOUT)
#@@logger.level = Logger::DEBUG
  
class Card
  RANKS =  [1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8]
    
  #1 Princess Annette (8), 
  #1 Countess Wilhelmina (7), 
  #1 King Arnaud IV (6), 
  #1 Prince Arnaud (5), 
  #2 Handmaid Susannah (4), 
  #2 Baron Talus (3), 
  #2 Priest Tomas (2), and 
  #5 Guard Odette (1)
  
  attr_accessor :rank

  def initialize(id)
    self.rank = RANKS[id % 16]  
  end
end

class Deck
  attr_accessor :cards
  def initialize
    # shuffle array and init each Card
    self.cards = (0..14).to_a.shuffle.collect { |id| Card.new(id) }
  end
end

class Rules_LoveLetter
	attr_accessor :pot, :turn, :j, :verbose
    attr_reader :howManyGames, :nbPlayers, :joueurs
    
    MAXTICKS = 300
    MAXTURNS = 15 # 24 tours dans une partie
    PASSE   = false
    PRENDRE = true
    

	def initialize(nbPlayers=4) #2 a 4 Joueurs
        @nbPlayers = nbPlayers	
        @pot, @turn, @j = 0, 0, 0
        @deck = Deck.new
        @ticks = 0
	end
	
    def start(howManyGames=1)
        #@deck.cards.each do |x|
        #    puts x.rank
        #end
        
        @howManyGames = howManyGames
                
        if howManyGames == 1
            @joueurs = [  #( rules, AI, Verbose=true)
                Joueur.new(self, AI_NoThanks_Bruno, true),                
                Joueur.new(self, AI_NoThanks_Bruno,true),                
            ]
            if @nbPlayers >= 3
                @joueurs.push Joueur.new(self, AI_NoThanks_Bruno, true)
            end
            
            if @nbPlayers == 4
                @joueurs.push Joueur.new(self, AI_NoThanks_Bruno, true)
            end
            @joueurs.shuffle!
            @currentPlayer = @joueurs[0]
                        
            @verbose = true
            
            #annoncerCarte #annonce la 1ere carte.
            
            #loop sur l'action ici.
            until @turn == MAXTURNS # 24 tours dans une partie
                #@ticks += 1 # surtout pour prevenir infinite loop :p
                
                #puts "-=- J#{@j+1} argent: #{@currentPlayer.money}$ Tableau: #{@currentPlayer.tableau}  Score: #{@currentPlayer.score} -=- T#{@turn+1}"                
                #puts "#{@pot}$ + # #{@deck.cards[@turn].rank } => J#{@j+1}"
                
                ###################
                
                adversaires = @joueurs
                adversaires.delete(@currentPlayer)
                
                a, b = @currentPlayer.play( @deck.cards[@turn], adversaires )
                action( a, b )
                
                if @turn < MAXTURNS
                    say " "
                    showTableaux
                    annoncerCarte            
                end
                
                @turn +=1 #tourne prochaine carte.	
                
                ###################                
                #if @currentPlayer.tableau.size < 0 then 
                #    puts "**** Houston on a un probleme. #{@currentPlayer.money} ****"
                #end
                
            end
                        
            puts "=========== GAME OVER ==========="
        
            @nbPlayers.times do |j|
                puts "-=- j#{j+1} #{format('%3s',@joueurs[j-1].score)} pts, Ai:#{@joueurs[j-1].nom} Tableau: #{@joueurs[j-1].tableau }"
            end             
                        
            if @ticks > MAXTICKS then 
                puts "**** max ticks atteint. da fuq? ****" 
            else
                say "Cartes manquantes #{extraCards}"
                winner                
                say "@game ticks = #{@ticks}"
            end
            
            
            
        else
        
        end
                
    end
    
    def play(carte)
    #1
  #1 
  #1 
  #1 
  #2 
 
  #2  
  #5 Guard Odette (1)
            case carte
            when 1
                puts "carte 1"
                @currentPlayer.assassinate
            when 2
                #Priest Tomas (2), look card
                @currentPlayer.spy
            when 3
            #  Baron Talus #3 (2x) Duel 
                @currentPlayer.duel
                puts "carte 3"
            when 4
            # Handmaid Susannah #4 (2x)  protect
                puts "carte 4"
            when 5
            # Prince Arnaud (5),   force card change
                puts "carte 5"
            when 6
            # King Arnaud IV (6),  change hand
                puts "carte 6"
    
            when 7
            #Countess Wilhelmina (7), must be dropped if king or prince.
                puts "carte 7"
                
            when 8
            # Princess Annette (8),  lost
                puts "carte 8"
                
            else
                puts "error carte invalide."
            end
        end
    
	def startOld(howManyGames=1)
        @howManyGames = howManyGames
                
        if howManyGames == 1
              @joueurs = [  #( rules, AI, Verbose=true)
                Joueur.new(self, AI_NoThanks_Patoo, true),
                Joueur.new(self, AI_NoThanks_Feld, true),
                Joueur.new(self, AI_NoThanks_Colovini, true)
            ]
            if @nbPlayers >= 4
                @joueurs.push Joueur.new(self, AI_NoThanks_Humain, true)
            end
            
            if @nbPlayers == 5
                @joueurs.push Joueur.new(self, AI_NoThanks_Bruno, true)
            end
            @joueurs.shuffle!
            @currentPlayer = @joueurs[0]
                        
            @verbose = true
            
            annoncerCarte #annonce la 1ere carte.
            
            #loop sur l'action ici.
            until @turn == MAXTURNS or @ticks > MAXTICKS # 24 tours dans une partie
                @ticks += 1 # surtout pour prevenir infinite loop :p
                
                #puts "-=- J#{@j+1} argent: #{@currentPlayer.money}$ Tableau: #{@currentPlayer.tableau}  Score: #{@currentPlayer.score} -=- T#{@turn+1}"                
                #puts "#{@pot}$ + # #{@deck.cards[@turn].rank } => J#{@j+1}"
                
                action( @currentPlayer.play( @deck.cards[@turn].rank, @pot, MAXTURNS-turn ) )
                
                if @currentPlayer.money < 0 then 
                    puts "**** Houston on a un probleme. #{@currentPlayer.money} ****"
                end
            end
                        
            puts "=========== GAME OVER ==========="
        
            @nbPlayers.times do |j|
                puts "-=- j#{j+1} #{format('%3s',@joueurs[j].score)} pts, #{@joueurs[j].money}$ Ai:#{@joueurs[j].nom} Tableau: #{@joueurs[j].tableau}"
            end             
                        
            if @ticks > MAXTICKS then 
                puts "**** max ticks atteint. da fuq? ****" 
            else
                say "Cartes manquantes #{extraCards}"
                winner                
                say "@game ticks = #{@ticks}"
            end
            
            
            
        else
        ######################
        ## Simulation Start ##
        ######################
            @joueurs = [                
                Joueur.new(self, AI_NoThanks_Colovini, false),
                Joueur.new(self, AI_NoThanks_Patoo, false),
                Joueur.new(self, AI_NoThanks_Feld, false),
                Joueur.new(self, AI_NoThanks_Colovini, false),                
                Joueur.new(self, AI_NoThanks_Bruno, false),
            ]
            @joueurs.shuffle!
            @currentPlayer = @joueurs[0]            
            @verbose = false
            puts "Simulation started.."
            
            
            
            howManyGames.times do |x|
                
                until @turn == MAXTURNS #24
                    action( @currentPlayer.play( @deck.cards[@turn].rank, @pot, MAXTURNS-turn ) )
                end
                @turn = 0
                #puts "** Game # #{x+1} Finished **"
              
                #inc = 
                #puts "#{inc-1}"
                #zz = gamesWon[inc]
                #puts "zz=#{zz}"
                                
                winner.winCount += 1 #garde le compte des victoires.
                
                # Assert que ya pas de infinite loop en cours.
                if @ticks > MAXTICKS 
                    puts "**** max ticks atteint. da fuq? ****" 
                end

                #RESET GAME
                @ticks = 0
                @deck.cards.shuffle!
                @joueurs.shuffle!                
                @joueurs.each {|j|  j.reset}
                
            end
            
            ### Simulation Results here
            puts ">>> Simulation results after #{howManyGames} games <<<<<<<<<<<<<<<<"
            
            #sort la list par nom.
            @joueurs.sort!{ |a,b| a.winCount <=> b.winCount }.reverse!
            
            @joueurs.each do |x|                
                puts "#{format('%2d', x.winCount.to_f / howManyGames * 100)}% #{format('%3d',x.winCount)} #{x.nom} "
            end
        end
	end
	
    def annoncerCarte
        puts
        say "=====#{MAXTURNS-turn}=======Offering card ##{@deck.cards[@turn].rank } ================="            
    end
    
    def showTableaux
        if howManyGames == 1
            table = []
            @joueurs.each do |x|
                table.push "#{x.nom} #{x.tableau}"
            end
            puts table.to_s
        end
    end
    
    def getSeat(player)
        @joueurs.index(player)+1
    end
    
    def action(choix, cible)
    
        pp "choix=#{choix}, cible=#{cible}"
        
        if choix == 0
        
		elsif choix == 1
            #carte 1
			say "#{@currentPlayer.nom} passe."
            #puts "increasing pot"
			@pot += 1
			@j = (@j+1) % @nbPlayers # Change de joueur.
			@currentPlayer = @joueurs[j]
            			
		
        
        else
            #carte 2        
            say "joue carte 2."
		end	
        
        return 0
        
    end
    
    def newCarte
        say "#{@currentPlayer.nom} joue."            
        #say "j#{@j+1} <-- #{@pot}$ + [#{@deck.cards[@turn].rank}]   #{@currentPlayer.tableau}"
        
        
                    
        
        
    end
    
    def winner
        highScore = 999
        gagnant   = nil
        

        @joueurs.each do |j|    
            #pp j.score
            if ( j.score < highScore )
                highScore = j.score
                gagnant = j
               
            end            
        end

        say "Le gagnant est #{gagnant.nom} j#{gagnant.seatPosition}"
        gagnant
    end
    
    def extraCards        
        
        return @deck.cards[14]
    
    end
    
    def say(text)
        if verbose
            puts "#{text}"
        end
    end
    
end

class AI_NoThanks
	attr_accessor :joueur
    
    #PASSE   = Rules_NoThanks::PASSE
    #PRENDRE = Rules_NoThanks::PRENDRE
    
	def initialize(joueur)
		joueur.nom="Roche"        
        @joueur = joueur                
	end
	
	def think( card, pot )
		if cash == 0
			puts "(Basic AI) Bankrupt."
			false
		end
	end
    
    def gotPrecedante?(card, tableau=@joueur.tableau)
        tableau.include?(card-1)
    end
    
    def gotSuivante?(card, tableau=@joueur.tableau)
        tableau.include?(card+1)
    end
    
    def isAdjacent?(card, tableau=@joueur.tableau)        
        gotPrecedante?(card, tableau) or gotSuivante?(card, tableau)        
    end
    
    def isHoleFiller?(card, tableau=@joueur.tableau)        
        gotPrecedante?(card, tableau) and gotSuivante?(card, tableau)
    end    
    
    def isHot?(card)
    #True si un autre joueur a la carte suivante ou precedante.
        
        @joueur.game.joueurs.each do |x|
            if x != @joueur and isAdjacent?(card, x.tableau)
                #puts "#{x.nom} got adjacent card."
                return true            
            end
        end
        
        false
    end
	
	def isAlive
        !isDead
		true
	end
	
	def isDead
        #true si les cartes precedante et suivante appartienne a d'autre joueurs.
		false
	end
	
	def isGood
	#carte avant et apres sont pas sorti	
	end
	
	def isAwesome
	#2 cartes avant et 2 cartes apres sont pas sorti
		
		if isGood
			#check pour les 2 autre  carte
			true
		end	
	end
    
    def isDisputed?(card)
        r=false
        if isAdjacent?(card) and isHot?(card)
            #puts "isDisputed = true"
            return true
        end
        
        return r
        
    end
end 

class AI_NoThanks_Bruno < AI_NoThanks
	attr_accessor :nom
	
	def initialize(joueur)
		super(joueur)
		joueur.nom="Bruno"
	end
	
    def think(main, adversaires)
		
        if main.size == 1
        #1st card so wait for second.
            0
        elsif false
            joueur.say "Conflit, je prend."
			PRENDRE
		elsif false
			#joueur.say "Je prend."
			PRENDRE
		elsif false
			joueur.say "Out of cash."
			PRENDRE
		else
			#joueur.say "Je joue."
			
            carte = main[0]
            main.delete(carte)
            cible = adversaires[0]
            
           return carte, cible
        end
	end
    
    
end

class AI_NoThanks_Patoo < AI_NoThanks
	attr_accessor :nom
	
	def initialize(joueur)
        super(joueur)
		joueur.nom="Patoo"
	end
    
	def think(card, pot, cardsLeft)
        #puts " #{pot} >  #{card}/2"
        
        if isDisputed?(card) 
            joueur.say "Conflit, je prend."
			PRENDRE
            
        elsif isAdjacent?(card)
            joueur.say "Youppi !!"
            PRENDRE
            
        elsif pot > card/2
            joueur.say "Moi prendre !"
            PRENDRE
            
        elsif joueur.money == 0
            joueur.say "no money!"
            PRENDRE
            
        else
            #joueur.say "Passe."
            PASSE
        end
	end    
end

class AI_NoThanks_Colovini < AI_NoThanks
	attr_accessor :nom
	
    GREEDYNESS_LEVEL = 14
    
	def initialize(joueur)
        super(joueur)
		joueur.nom="Colovini"
	end
    
	def think(card, pot, cardsLeft)
        #puts " #{pot} >  #{card}/2"
       		
        if isDisputed?(card) 
            joueur.say "Conflit, je prend."
			PRENDRE
        elsif isAdjacent?(card) and pot > 3
            joueur.say "Free card!"
            PRENDRE                    
        elsif pot > (card/4+2)
            joueur.say "... (prend la carte)"
            PRENDRE
        
        elsif joueur.money == 0
            joueur.say "no money!"
            PRENDRE
        else
            #joueur.say "Passe."
            PASSE
        end
	end
    
    
end

class AI_NoThanks_Feld < AI_NoThanks
#prend une carte juste quand ya pu une cenne ou que la carte vaut 0+
	attr_reader :nom, :cardsLeft
	
    LOWCASH = 6
    
	def initialize(joueur)
        super(joueur)
		joueur.nom="Stefan"
        
        @cardsLeft=nil
	end
	
	def think(card, cardsLeft)
        
		#puts "(AI) carte: #{card}, valeur mise: #{value}, cash: #{cash}"
		if isDisputed?(card) 
            joueur.say "Conflit, je prend."
			PRENDRE
            
        elsif isAdjacent?(card) and pot > (card * 0.30)
            joueur.say "Schadenffreude!!!"
			PRENDRE
		
        elsif pot >= (card/2)
			joueur.say "<3 cette carte !"
			PRENDRE
       
        #Si low on cash then take a medium or lower card with some cash on it
        elsif joueur.money < LOWCASH and 
            ((card < 30 and pot > 6) or ( card <20 and pot > 4 ) or (card < 14 and pot != 0)) and
            cardsLeft > 3

			joueur.say "(low on cash) Je prend ca !"
			PRENDRE
    		
        elsif joueur.money == 0
			joueur.say "* Feld has Failed. no cash !! *"
			PRENDRE
		
		else        
			if isAdjacent?(card)
				joueur.say "C'est juste bon pour moi on va faire tourner un peu.. [#{joueur.money}$] #{joueur.tableau}$"
			else
				joueur.say "Non merci. v#{card-pot} [#{joueur.money}]$ #{joueur.tableau}"           
			end
			PASSE
		end
	end
	
	def myValue(card, pot)
        
		if isAdjacent(card)
			puts "*** est la !!"
			myValue -= card + 1
		#elsif tableau.include? card-1
		#	myValue = 0
		#elsif tableau.include? card+1
		#	myValue = -1
		else
			puts "pas la"
			myValue = card
		end
		myValue = 45
		#myValue -= pot
		joueur.say "Pour moi cette carte vaut #{myValue} pts."
		
		return myValue
	end
	

end

class AI_NoThanks_Humain < AI_NoThanks
    attr_reader :nom, :cardsLeft
    
    def initialize(joueur)
        super(joueur)
        joueur.nom="Johnny"
        @cardsLeft=nil
    end
    
    def think(card, pot, cardsLeft)
     
        puts "Voulez-vous la carte #{card} et #{pot}$  (#{joueur.money}$)"
        choix = $stdin.gets
        choix.strip!.upcase!
        
        #puts "vous avez choisi : *#{choix}*"
        if choix == 'Y' and joueur.money > 0
            #p "vous prenez la carte."
            PRENDRE
        
        else
            #p "Vous passez."
            PASSE
        end
    end    
    
end

class Joueur
    attr_accessor :ai,  :nom , :verbose, :winCount
    attr_reader   :tableau, :money, :game
  
	def initialize( rules, ai_init=AI_NoThanks_Basic, verbose=true, cash=11)
        @nom="John Doe"
        @game = rules
        @tableau = []
        @main = []
        @ai = ai_init.new(self)
        @money = cash
        @verbose = verbose
        @winCount = 0
	end
	
    def seatPosition
        @game.getSeat(self)
    end
    
	def play(newCard, adversaires)        
		#pige
        @main.push newCard
        puts "The card is #{newCard.rank}"
        c, t = ai.think(@main, adversaires)
        puts "**"
        pp "#{carte}"
        puts "#{cible.nom}"
        puts "**"
        tableau.push @main.delete(carte)
        play = carte, cible        
	end
	
	def score
		score = -money 
		derniereCarte=0
		
		tableau.each do |carte|
			if carte != derniereCarte+1
				score += carte
			end
			
			derniereCarte = carte
		end
		#@@logger.debug score
		
		return 10
	end
    
    def say(text)
        if verbose
            puts "#{seatPosition}:(AI #{nom}) #{text}"
        end
    end
    
    def reset       
        @tableau.clear
        @money = 11        
    end
    
    #def to_s
    #    value = tableau
    #end
end



if __FILE__ == $0	
	game = Rules_LoveLetter.new.start
end

__END__

* TODO ***
- (done) faire la class JoueurHumain done. 9avril2013. 
- (done) fait fonctionner la simulation (n games).
- fonction isAlive.
- fonction isAwesome.
- je crois que player ne devrais pas connaitre "aucune" regles.

AI todo's (qui serait le fun, mais qui arrivera jamais prolly)

- receive hesitation time for since beginning of card, learn from it.
- approx money left.
- full counting money.

**********
class value prend un joueur et un carte et retourne la valeur brut de cette carte pour ce joueur.

**process**
ruleset envoi la carte au joueur.  joueur appel AI  AI fait le thinking 
